package chat

import (
	"fmt"
	"log"
	"net"
)

const (
	MAXCLIENTS = 50
)

type ClienTable map[net.Conn]*Client
type Message chan string
type Token chan int

type Server struct {
	listener net.Listener  // listener
	clients  ClienTable    // clients集合
	tokens   Token         // 当有用户连接的时候，发送channel
	pending  chan net.Conn // 用于发送用户连接到pending channel
	quiting  chan net.Conn // 退出channel
	incoming Message       // 接受消息channel
	outgoing Message       //发送消息channel
}

func NewServer() *Server {
	server := &Server{
		clients:  make(ClienTable, MAXCLIENTS),
		tokens:   make(Token, MAXCLIENTS),
		pending:  make(chan net.Conn),
		quiting:  make(chan net.Conn),
		incoming: make(Message),
		outgoing: make(Message),
	}

	server.listen()
	return server
}

func (server *Server) listen() {
	go func() {
		for {
			select {
			case message := <-server.incoming:
				server.broadcast(message)
			case conn := <-server.pending:
				server.join(conn)
			case conn := <-server.quiting:
				server.leave(conn)
			}
		}
	}()
}

func (server *Server) broadcast(message string) {
	log.Printf("Brocasting message:%s\n", message)

	for _, client := range server.clients {
		client.outgoing <- message
	}
}

func (server *Server) join(conn net.Conn) {
	client := NewClient(conn)
	name := getUniqName()
	client.SetName(name)
	server.clients[conn] = client

	log.Printf("auto assigned name for conn %p:%s\n", conn, name)

	go func() {
		for {
			select {
			case msg := <-client.incoming:
				log.Printf("Got message: %s from client %s\n", msg, client.Name())
				server.incoming <- fmt.Sprintf("%s says %s\n", client.Name(), msg)
			}
		}
	}()

	go func() {
		for {
			select {
			case conn := <-client.quiting:
				log.Printf("Client %s is quiting\n", client.Name())
				server.quiting <- conn
			}
		}
	}()
}

func (server *Server) leave(conn net.Conn) {
	if conn != nil {
		conn.Close()
		delete(server.clients, conn)
	}
	server.generateToken()
}

func (self *Server) Start(connString string) {
	var err error = nil
	self.listener, err = net.Listen("tcp", connString)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Server %p start ! \n", self)

	for i := 0; i < MAXCLIENTS; i++ {
		self.generateToken()
	}

	for {
		conn, err := self.listener.Accept()
		if err != nil {
			log.Println(err)
			return
		}

		log.Printf("A new connection %v kicks\n", conn)
		self.takeToken()
		self.pending <- conn
	}
}

func (self *Server) generateToken() {
	self.tokens <- 0
}
func (self *Server) takeToken() {
	<-self.tokens
}
